package com.seeyon.apps.kkSample.utils.http;

import com.alibaba.fastjson.JSONObject;
import com.seeyon.apps.kkSample.utils.BasicDataUtil;
import com.seeyon.apps.kkSample.utils.MyX509TrustManager;
import com.seeyon.ctp.util.UUIDLong;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import java.io.*;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.Charset;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.Map.Entry;

/**
 * @description HTTP REST 请求帮助类
 */
public class HttpRestUtil {
    private static final Log LOGGER = LogFactory.getLog(HttpRestUtil.class);
    // 编码格式。发送编码格式统一用UTF-8
    private static final String ENCODING = "UTF-8";
    private static PoolingHttpClientConnectionManager cm;
    private static String EMPTY_STR = "";


    private static void init() {
        if (cm == null) {
            cm = new PoolingHttpClientConnectionManager();
            cm.setMaxTotal(50);// 整个连接池最大连接数
            cm.setDefaultMaxPerRoute(5);// 每路由最大连接数，默认值是2
        }
    }

    /**
     * 通过连接池获取HttpClient
     */
    private static CloseableHttpClient getHttpClient() {
        init();
        return HttpClients.custom().setConnectionManager(cm).build();
    }

    /**
     * get 请求
     *
     * @param URL
     * @param headers
     * @param params
     * @return
     * @throws URISyntaxException
     * @throws IOException
     */
    public static String doGet(String URL, Map<String, Object> headers, Map<String, Object> params) throws URISyntaxException, IOException {
        //创建 httpclient 对象
        //CloseableHttpClient httpClient = HttpClients.createDefault();

        //创建访问地址
        URIBuilder uriBuilder = new URIBuilder(URL);
        uriBuilder = packageGetParams(params, uriBuilder);

        //创建 http 对象
        HttpGet httpGet = new HttpGet(uriBuilder.build());
        packageHeader(headers, httpGet);
        // 创建httpResponse对象
        //CloseableHttpResponse httpResponse = null;
        return getResult(httpGet);

    }

    /**
     * 封装  get 请求参数
     *
     * @param params
     * @param uriBuilder
     * @return
     */
    private static URIBuilder packageGetParams(Map<String, Object> params, URIBuilder uriBuilder) {
        if (params != null) {
            Set<Entry<String, Object>> entrySet = params.entrySet();
            for (Entry<String, Object> entry : entrySet) {
                uriBuilder.setParameter(entry.getKey(), entry.getValue().toString());
            }
        }
        return uriBuilder;
    }

    /**
     * post 请求 json
     *
     * @param URL
     * @param headers
     * @param params
     * @return
     * @throws IOException
     */
    public static String doPost(String URL, Map<String, Object> headers, Map<String, Object> params) throws IOException {
        HttpPost httpPost = new HttpPost(URL);
        packageHeader(headers, httpPost);
        packagePostParams(params, httpPost);
        return getResult(httpPost);

    }

    /**
     * 封装 post 请求参数
     * application/json
     *
     * @param params
     * @param httpMethod
     * @throws UnsupportedEncodingException
     */
    private static void packagePostParams(Map<String, Object> params, HttpEntityEnclosingRequestBase httpMethod) throws UnsupportedEncodingException {
        if (params != null) {
//			String parames = "{\"corpAccessToken\": \"818652251B353A2803358E9402771E74\",\"corpId\": \"FSCID_EEB704D56A4BCDEEDDEC72A41474FCDD\",\"currentOpenUserId\": \"FSUID_76E4D40E38B9DEFFD69623954F5FDEE8\",\"data\": {\"object_data\": {\"dataObjectApiName\": \"ProductObj\",\"name\": \"测试2021122901\",\"unit\":\"2\",\"owner\":[\"FSUID_28F21B3B0757025D2BF1ABC401E4274B\"],\"price\":\"0\",\"product_code\":\"A\",\"category\":\"23\",\"product_status\":\"1\",\"field_9h0k1__c\": \"测试\",\"gengxin_Date__c\": 1640764564379}}}";
            String paramsStr = JSONObject.toJSONString(params);


//			StringEntity entity = new StringEntity(parames, "UTF-8");
//			httpMethod.setEntity(entity);
//			设置请求到  http 对象中
            StringEntity entity = new StringEntity(paramsStr, "UTF-8");
            entity.setContentType("application/json");
            httpMethod.setEntity(entity);
        }

    }

    /**
     * post 请求 json
     * application/x-www-form-urlencoded
     *
     * @param URL
     * @param headers
     * @param params
     * @return
     * @throws IOException
     */
    public static String doPostForm(String URL, Map<String, Object> headers, Map<String, Object> params) throws IOException {
        HttpPost httpPost = new HttpPost(URL);
        packageHeader(headers, httpPost);
        packagePostFormParams(params, httpPost);
        return getResult(httpPost);

    }

    /**
     * 封装 post 请求参数
     * 以 application/x-www-form-urlencoded 发送请求
     *
     * @param params
     * @param httpMethod
     * @throws UnsupportedEncodingException
     */
    private static void packagePostFormParams(Map<String, Object> params, HttpEntityEnclosingRequestBase httpMethod) throws UnsupportedEncodingException {
        if (params != null) {
            List<NameValuePair> nvps = new ArrayList<>();
            //Set<Entry<String, Object>> entrySet = params.entrySet();
            for (Entry<String, Object> entry : params.entrySet()) {
                nvps.add(new BasicNameValuePair(entry.getKey(), String.valueOf(entry.getValue())));
            }
            //设置请求到  http 对象中
            httpMethod.setEntity(new UrlEncodedFormEntity(nvps, ENCODING));
        }
    }

    /**
     * 封装请求头
     *
     * @param params
     * @param httpMethod
     */
    private static void packageHeader(Map<String, Object> params, HttpRequestBase httpMethod) {
        if (params != null) {
            Set<Entry<String, Object>> entrySet = params.entrySet();
            for (Entry<String, Object> entry : entrySet) {
                // 设置到请求头到HttpRequestBase对象中
                httpMethod.setHeader(entry.getKey(), entry.getValue().toString());
            }
        }
    }

    /**
     * 附件上传
     *
     * @param URL
     * @param fileList
     * @return
     * @throws IOException
     */
    public static String uploadFileByHPClient(String URL, List<Map<String, Object>> fileList) throws IOException {
        HttpPost httpPost = new HttpPost(URL);
        retInpStreamMul(httpPost, fileList);

        return getResult(httpPost);

    }

    /**
     * 通过文件链接下载，获取InputStream
     *
     * @param urlStr 链接地址
     * @return
     */
    public static InputStream doConForFile(String urlStr) {
        InputStream inputStream = null;
        try {
            // 创建SSLContext
            SSLContext sslContext = SSLContext.getInstance("SSL");
            TrustManager[] tm = {new MyX509TrustManager()};
            // 初始化
            sslContext.init(null, tm, new java.security.SecureRandom());
            // 获取SSLSocketFactory对象
            SSLSocketFactory ssf = sslContext.getSocketFactory();
            // url对象
            URL url = new URL(urlStr);
            // 打开连接
            HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
            /*
             * 这一步的原因: 当访问HTTPS的网址。您可能已经安装了服务器证书到您的JRE的keystore
             * 但是服务器的名称与证书实际域名不相等。这通常发生在你使用的是非标准网上签发的证书。
             *
             * 解决方法：让JRE相信所有的证书和对系统的域名和证书域名。
             *
             * 如果少了这一步会报错:java.io.IOException: HTTPS hostname wrong: should be <localhost>
             */
            conn.setHostnameVerifier(new MyX509TrustManager().new TrustAnyHostnameVerifier());
            // 设置一些参数
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);
            // 设置当前实例使用的SSLSoctetFactory
            conn.setSSLSocketFactory(ssf);
            conn.connect();

            // 得到输入流
            inputStream = conn.getInputStream();

        } catch (NoSuchAlgorithmException e) {
            LOGGER.error("E签宝下载签署文件失败 {}:" + e);
        } catch (KeyManagementException e) {
            LOGGER.error("E签宝下载签署文件失败 {}:" + e);
        } catch (IOException e) {
            LOGGER.error("E签宝下载签署文件失败 {}:" + e);
        }

        return inputStream;

    }

    /**
     * 组装 httpclient 上传附件时必传参数
     * MultipartEntityBuilder
     *
     * @param httpPost
     * @param fileList
     */
    private static void retInpStreamMul(HttpPost httpPost, List<Map<String, Object>> fileList) {

        //Header
        Long haha = UUIDLong.longUUID();
        String boundary = "--------------" + haha;
        httpPost.setHeader("Content-Type", "multipart/form-data; boundary=" + boundary);
        //Entity
        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
        //字符编码
        builder.setCharset(Charset.forName("UTF-8"));
        //模拟浏览器
        builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
        builder.setBoundary(boundary);

//		MultiValueMap<String, Object> param = new LinkedMultiValueMap<>();
        //组装文件
        if (fileList != null && !fileList.isEmpty()) {
            for (Map<String, Object> fileMap : fileList) {
                String fileName = fileMap.get("fileName").toString();
                String fileSize = fileMap.get("fileLength").toString();
                FileInputStream inputStream = (FileInputStream) fileMap.get("inputStream");
//				param.add("media",inputStream);
                //inputStream 流传输
                builder.addBinaryBody("media", inputStream, ContentType.MULTIPART_FORM_DATA, fileName);
                //文件名称
                builder.addTextBody("filename", fileName);
                //文件大小
                builder.addTextBody("filelength", fileSize);
            }

        }
//		RestTemplate request = new RestTemplate();
//		HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(param, headers);
//		ResponseEntity<Map> response = request.postForEntity(URL, requestEntity, Map.class);

        HttpEntity entity = builder.build();
        httpPost.setEntity(entity);
    }

    /**
     * 组装 httpclient 上传附件时必传参数
     *
     * @param httpPost
     * @param fileList
     */
    private static void retFileMul(HttpPost httpPost, List<Map<String, Object>> fileList) {
        //Header
        Long haha = UUIDLong.longUUID();
        String boundary = "--------------" + haha;
        httpPost.setHeader("Content-Type", "multipart/form-data; boundary=" + boundary);

        //Entity
        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
        //字符编码
        builder.setCharset(Charset.forName("UTF-8"));
        //模拟浏览器
        builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
        builder.setBoundary(boundary);
        //组装文件
        if (fileList != null && !fileList.isEmpty()) {
            for (Map<String, Object> fileMap : fileList) {

                String fileName = BasicDataUtil.kvToStr(fileMap, "fileName");
                File newFile = new File(BasicDataUtil.kvToStr(fileMap, "file"));
                builder.addPart("media", new FileBody(newFile));
            }
        }
        HttpEntity entity = builder.build();
        httpPost.setEntity(entity);
    }


    /**
     * 处理Http请求
     * 返回响应结果
     *
     * @param request
     * @return
     * @throws IOException
     */
    private static String getResult(HttpRequestBase request) throws IOException {
        // CloseableHttpClient httpClient = HttpClients.createDefault();
        RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(60000)
                .setConnectionRequestTimeout(60000).setSocketTimeout(60000).build();
        request.setConfig(requestConfig);//
        CloseableHttpClient httpClient = getHttpClient();
        CloseableHttpResponse response = httpClient.execute(request);
        // response.getStatusLine().getStatusCode();
        HttpEntity entity = response.getEntity();
        if (entity != null) {
            // long len = entity.getContentLength();// -1 表示长度未知
            String result = EntityUtils.toString(entity, ENCODING);
            response.close();
            // httpClient.close();
            return result;
        }
        return EMPTY_STR;
    }


    /**
     * post 请求 json
     * 请求文件下载接口   请求对方下载附件接口，直接拿到文件流
     *
     * @param URL
     * @param headers
     * @param params
     * @return
     * @throws IOException
     */
    public static Map<String, Object> doPostForFile(String URL, Map<String, Object> headers, Map<String, Object> params) throws IOException {
        HttpPost httpPost = new HttpPost(URL);
        packageHeader(headers, httpPost);
        packagePostParams(params, httpPost);
        return getResultForFile(httpPost);

    }

    /**
     * 请求文件下载接口，获取返回的文件流
     *
     * @param request
     * @return
     * @throws IOException
     */
    private static Map<String, Object> getResultForFile(HttpRequestBase request) throws IOException {
        // CloseableHttpClient httpClient = HttpClients.createDefault();
        RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(60000)
                .setConnectionRequestTimeout(60000).setSocketTimeout(60000).build();
        request.setConfig(requestConfig);//
        CloseableHttpClient httpClient = getHttpClient();
        CloseableHttpResponse response = httpClient.execute(request);
        // response.getStatusLine().getStatusCode();
        HttpEntity entity = response.getEntity();

        if (entity != null) {
            Map<String, Object> ret = new HashMap<>();
            String filename = getFileName(response);//文件名
            InputStream stream = entity.getContent();//文件流

            ret.put("fileName", filename);
            ret.put("fileStream", stream);
            return ret;
        }
        return null;
    }

    /**
     * 获取response header中Content-Disposition中的filename值
     *
     * @param response
     * @return
     */
    private static String getFileName(CloseableHttpResponse response) {
        Header contentHeader = response.getFirstHeader("Content-Disposition");
        String filename = "";
        if (contentHeader != null) {
            HeaderElement[] values = contentHeader.getElements();
            if (values.length == 1) {
                NameValuePair param = values[0].getParameterByName("filename");
                if (param != null) {
                    //filename = new String(param.getValue().toString().getBytes(), "utf-8");
                    //filename=URLDecoder.decode(param.getValue(),"utf-8");
                    filename = param.getValue();
                }
            }
        }
        return filename;
    }


    /////////////////////////////////////////////////////////////////////////////////

    /**
     * 解析 json 第一层
     *
     * @param resultJ
     * @param keyword
     * @return
     */
    public static String firstFloorJ(String resultJ, String keyword) {
        Map<String, Object> resJson = (Map<String, Object>) JSONObject.parseObject(resultJ);
        String getValue = resJson.get(keyword) == null ? "" : resJson.get(keyword).toString();
        return getValue;
    }

    /**
     * 解析 json 第二层
     *
     * @param resultJ
     * @param keyword
     * @return
     */
    public static String theSecondFloorJ(String resultJ, String data, String keyword) {
        Map<String, Object> resJsonMap = JSONObject.parseObject(resultJ);
        Map<String, Object> dataMap = resJsonMap.get(data) == null ? null : (Map<String, Object>) resJsonMap.get(data);
        String getValue = dataMap.get(keyword) == null ? "" : dataMap.get(keyword).toString();
        return getValue;

    }


}
